
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
#include <unistd.h>

#include <errno.h>

#include <netinet/tcp.h>

#include <libifconfig.h>

#include <libepoll.h>

#include <iostream>
#include <list>

#define FILE_MAX_LEN				1024

enum handle_type
{
	HANDLE_TYPE_NORMAL = 0,
	HANDLE_TYPE_MAIN,
	HANDLE_TYPE_SERVICE,
	HANDLE_TYPE_PIPE,

	HANDLE_TYPE_TCP_BUTT
};

static struct handle_list
{
	enum handle_type type;

	void *p;
} handle_list[FILE_MAX_LEN];

enum pipe_type
{
	PIPE_TYPE_WAIT = 0,

	PIPE_TYPE_SERVER,
	PIPE_TYPE_CLIENT,

	PIPE_TYPE_BUTT
};

struct handle_service
{
	char xcode[64];

	int fd;

	std::list<int> wait;
};

struct handle_pipe
{
	enum pipe_type type;

	int listen_fd;
	int pipe_fd;
};

static std::list<struct handle_service> m_service;

static int find_service(const char *xcode)
{
	int fd = -1;

	if(!m_service.empty())
	{
		for(std::list<struct handle_service>::iterator iter = m_service.begin(); iter != m_service.end(); ++iter)
		{
			if(0 == strcmp(xcode, iter->xcode))
			{
				fd = iter->fd;

				break;
			}
		}
	}

	return fd;
}

static bool init_list(Lib_Epoll *epoll, int fd, enum handle_type type)
{
	bool ret = false;

	if(NULL == epoll)
	{
		goto out;
	}

	if(0 > fd)
	{
		goto out;
	}

	if(HANDLE_TYPE_NORMAL >= type || HANDLE_TYPE_TCP_BUTT <= type)
	{
		goto out;
	}

	if(HANDLE_TYPE_NORMAL != handle_list[fd].type)
	{
		goto out;
	}

	if(NULL != handle_list[fd].p)
	{
		goto out;
	}

	switch(type)
	{
		case HANDLE_TYPE_SERVICE:
			{
				struct handle_service *handle = new struct handle_service();

				if(NULL != handle)
				{
					strncpy(handle->xcode, "12345678", sizeof(handle->xcode));
					handle->fd = fd;

					m_service.push_back(*handle);

					handle_list[fd].p = handle;

					ret = true;
				}
			}
			break;
		case HANDLE_TYPE_PIPE:
			{
				struct handle_pipe *handle = new struct handle_pipe();

				if(NULL != handle)
				{
					handle->type = PIPE_TYPE_WAIT;
					handle->pipe_fd = -1;

					handle_list[fd].p = handle;

					ret = true;
				}
			}
			break;
		default:
			{
				handle_list[fd].p = NULL;

				ret = true;
			}
			break;
	}

	if(true == ret)
	{
		if(true != epoll->add(fd))
		{
			goto out;
		}

		handle_list[fd].type = type;
	}

out:
	return ret;
}

static void deinit_list(Lib_Epoll *epoll, int fd)
{
	if(0 <= fd)
	{
		enum handle_type type = handle_list[fd].type;
		void *p = handle_list[fd].p;

		handle_list[fd].type = HANDLE_TYPE_NORMAL;
		handle_list[fd].p = NULL;

		if(NULL != p)
		{
			switch(type)
			{
				case HANDLE_TYPE_SERVICE:
					{
						struct handle_service *handle = (struct handle_service *)p;

						delete handle;
						handle = NULL;

						p = NULL;
					}
					break;
				case HANDLE_TYPE_PIPE:
					{
						struct handle_pipe *handle = (struct handle_pipe *)p;

						if(0 <= handle->pipe_fd)
						{
							deinit_list(epoll, handle->pipe_fd);
						}

						delete handle;
						handle = NULL;

						p = NULL;
					}
					break;
				default:
					printf("%s: default !!!\n", __func__);
					break;
			}
		}

		if(NULL != epoll)
		{
			epoll->del(fd);
		}

		close(fd);
		fd = -1;
	}
}

static void handle_socket(void *arg, int fd)
{
	Lib_Epoll *epoll = (Lib_Epoll *)arg;

	switch(handle_list[fd].type)
	{
		case HANDLE_TYPE_MAIN:
			{
				int client_fd = accept(fd, NULL, NULL);

				if(0 <= client_fd)
				{
					if(true == init_list(epoll, client_fd, HANDLE_TYPE_PIPE))
					{
						printf("init_list -> CLIENT_TYPE_PIPE: %d, ok\n", client_fd);
					}
					else
					{
						close(client_fd);
						client_fd = -1;

						printf("init_list -> CLIENT_TYPE_PIPE failed\n");
					}
				}
			}
			break;
		case HANDLE_TYPE_SERVICE:
			{
				int client_fd = accept(fd, NULL, NULL);

				if(0 <= client_fd)
				{
					struct handle_service *service = (struct handle_service *)handle_list[fd].p;

					if(NULL != service)
					{
						if(!service->wait.empty())
						{
							printf("\033[31;1mwait size: %ld\033[0m\n", service->wait.size());

							if(true == init_list(epoll, client_fd, HANDLE_TYPE_PIPE))
							{
								struct handle_pipe *pipe0 = (struct handle_pipe *)handle_list[client_fd].p;

								if(NULL != pipe0)
								{
									int remote_fd = -1;

									{
										std::list<int>::iterator iter = service->wait.begin();

										remote_fd = *iter;

										service->wait.erase(iter);
									}

									struct handle_pipe *pipe1 = (struct handle_pipe *)handle_list[remote_fd].p;

									if(NULL != pipe1)
									{
										pipe0->type = PIPE_TYPE_CLIENT;
										pipe0->pipe_fd = remote_fd;

										pipe1->pipe_fd = client_fd;

										printf("\033[32;1mnew link create ok\033[0m\n");
									}
									else
									{
										printf("\033[31;1mpipe1 failed\033[0m\n");

										deinit_list(epoll, client_fd);
									}
								}
								else
								{
									printf("\033[31;1mpipe0 failed\033[0m\n");

									deinit_list(epoll, client_fd);
								}
							}
							else
							{
								printf("\033[31;1minit_list failed\033[0m\n");

								close(client_fd);
								client_fd = -1;
							}
						}
						else
						{
							printf("\033[31;1mwait list empty\033[0m\n");

							close(client_fd);
							client_fd = -1;
						}
					}
					else
					{
						printf("\033[31;1mservice failed\033[0m\n");

						close(client_fd);
						client_fd = -1;
					}
				}
				else
				{
					printf("\033[31;1maccept failed\033[0m\n");
				}
			}
			break;
		case HANDLE_TYPE_PIPE:
			{
				char buffer[2048] = {0};

				int ret = ::recv(fd, buffer, sizeof(buffer), 0);

				struct handle_pipe *pipe = (struct handle_pipe *)handle_list[fd].p;

				if(NULL != pipe)
				{
					if(0 < ret)
					{
						switch(pipe->type)
						{
							case PIPE_TYPE_WAIT:
								{
									int listen_fd = find_service(buffer);

									if(0 <= listen_fd)
									{
										printf("find_service success: %d\n", listen_fd);

										struct handle_service *service = (struct handle_service *)handle_list[listen_fd].p;

										if(NULL != service)
										{
											printf("push_back: %d\n", fd);

											service->wait.push_back(fd);

											pipe->type = PIPE_TYPE_SERVER;
											pipe->listen_fd = listen_fd;
											pipe->pipe_fd = -1;
										}
									}
									else
									{
										printf("switch type wait, bind_service failed, pipe fd, close!!!\n");

										deinit_list(epoll, fd);
									}
								}
								break;
							case PIPE_TYPE_SERVER:
							case PIPE_TYPE_CLIENT:
								{
									if(0 <= pipe->pipe_fd)
									{
										::send(pipe->pipe_fd, buffer, ret, 0);
									}
								}
								break;
							default:
								{
									printf("switch default, pipe fd, close!!!\n");

									deinit_list(epoll, fd);
								}
								break;
						}
					}
					else
					{
						printf("recv failed, pipe fd, close by %d!!!\n", pipe->type);

						deinit_list(epoll, fd);
					}
				}
				else
				{
					printf("invalid handle, pipe fd, close!!!\n");

					deinit_list(epoll, fd);
				}
			}
			break;
		default:
			{
				char buffer[2048] = {0};

				::recv(fd, buffer, sizeof(buffer), 0);

				printf("default fd, close!!!\n");

				deinit_list(epoll, fd);
			}
			break;
	}
}

static int listen_on(const char *ip, unsigned short port)
{
	int listen_fd = -1;

	struct sockaddr_in local_addr;

	if(NULL == ip || 0 == port)
	{
		goto out;
	}

	listen_fd = socket(AF_INET, SOCK_STREAM, 0);

	if(0 > listen_fd)
	{
		printf("%s: create socket failed: %s\n", __func__, strerror(errno));

		goto out;
	}

	if(0 != Lib_IfConfig::setSocketReUseAddr(listen_fd))
	{
		printf("%s: setsocket failed: %s\n", __func__, strerror(errno));

		goto out1;
	}

	memset(&local_addr, 0, sizeof(local_addr));

	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(port);
	local_addr.sin_addr.s_addr = inet_addr(ip);

	if(0 != bind(listen_fd, (struct sockaddr *)&local_addr, sizeof(local_addr)))
	{
		printf("%s: bind failed: %s\n", __func__, strerror(errno));

		goto out1;
	}

	if(0 != listen(listen_fd, 5))
	{
		printf("%s: listen failed: %s\n", __func__, strerror(errno));

		goto out1;
	}

	goto out;

out1:
	if(0 <= listen_fd)
	{
		close(listen_fd);
		listen_fd = -1;
	}
out:
	return listen_fd;
}

static bool get_if_addr(const char *ifname, char *buffer, int size)
{
	in_addr_t ifaddr;

	if(0 == Lib_IfConfig::getInterfaceIpAddr(ifname, &ifaddr))
	{
		snprintf(buffer, size, "%u.%u.%u.%u", ((unsigned char *)&ifaddr)[0], \
												((unsigned char *)&ifaddr)[1], \
												((unsigned char *)&ifaddr)[2], \
												((unsigned char *)&ifaddr)[3]);

		return true;
	}

	return false;
}

int main(int argc, char **argv)
{
	int oc = 0;

	char ifaddr[32] = {0};
	unsigned short port = 0;

	Lib_Epoll *epoll = NULL;

	int listen_fd = -1;

	int service_fd = -1;

	if(1 == argc)
	{
		printf("-i\tinterface\n-p port\n");
	}

	while(0 < (oc = getopt(argc, argv, "i:p:")))
	{
		switch(oc)
		{
			case 'i':
				if(true != get_if_addr(optarg, ifaddr, sizeof(ifaddr)))
				{
					goto out;
				}
				break;
			case 'p':
				port = (unsigned short)strtoul(optarg, NULL, 0);
				if(0 == port)
				{
					goto out;
				}
				break;
			case '?':
				goto out;
				break;
			case ':':
				goto out;
				break;
		}
	}

	printf("ip: %s:%d\n", ifaddr, port);

	epoll = Lib_Epoll::getObject(512);

	if(NULL == epoll)
	{
		goto out;
	}

	printf("epoll ok\n");

	listen_fd = listen_on(ifaddr, port);

	if(0 > listen_fd)
	{
		goto out1;
	}

	printf("listen_on %d ok\n", port);

	if(true != init_list(epoll, listen_fd, HANDLE_TYPE_MAIN))
	{
		goto out2;
	}

	service_fd = listen_on(ifaddr, port + 1);

	if(0 > service_fd)
	{
		goto out1;
	}

	printf("listen_on %d ok\n", port + 1);

	if(true != init_list(epoll, service_fd, HANDLE_TYPE_SERVICE))
	{
		goto out2;
	}

	printf("init_list ok\n");

	while(epoll->loop())
	{
		epoll->wait(1000, handle_socket, epoll);
	}

out2:
	if(0 <= listen_fd)
	{
		close(listen_fd);
		listen_fd = -1;
	}
out1:
	if(NULL != epoll)
	{
		delete epoll;
		epoll = NULL;
	}
out:
	return 0;
}
